// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/targets/ohos.dart';
import 'package:flutter_tools/src/convert.dart';

import '../../../src/common.dart';
import '../../../src/context.dart';
import '../../../src/fake_process_manager.dart';

void main() {
  late FakeProcessManager processManager;
  late FileSystem fileSystem;
  late Artifacts artifacts;
  late Logger logger;

  setUp(() {
    logger = BufferLogger.test();
    fileSystem = MemoryFileSystem.test();
    processManager = FakeProcessManager.empty();
    artifacts = Artifacts.test();
  });

  testUsingContext('debug bundle contains expected resources', () async {
    final Environment environment = Environment.test(
      fileSystem.currentDirectory,
      outputDir: fileSystem.directory('out')..createSync(),
      defines: <String, String>{
        kBuildMode: 'debug',
      },
      processManager: processManager,
      artifacts: artifacts,
      fileSystem: fileSystem,
      logger: logger,
    );
    environment.buildDir.createSync(recursive: true);

    // create pre-requisites.
    environment.buildDir.childFile('app.dill').writeAsStringSync('abcd');
    fileSystem
        .file(artifacts.getArtifactPath(Artifact.vmSnapshotData,
            mode: BuildMode.debug))
        .createSync(recursive: true);
    fileSystem
        .file(artifacts.getArtifactPath(Artifact.isolateSnapshotData,
            mode: BuildMode.debug))
        .createSync(recursive: true);

    await const DebugOhosApplication().build(environment);

    expect(
        fileSystem
            .file(fileSystem.path
                .join('out', 'flutter_assets', 'isolate_snapshot_data'))
            .existsSync(),
        true);
    expect(
        fileSystem
            .file(fileSystem.path
                .join('out', 'flutter_assets', 'vm_snapshot_data'))
            .existsSync(),
        true);
    expect(
        fileSystem
            .file(fileSystem.path
                .join('out', 'flutter_assets', 'kernel_blob.bin'))
            .existsSync(),
        true);
  });

  testUsingContext('debug bundle contains expected resources with bundle SkSL',
      () async {
    final Environment environment = Environment.test(
      fileSystem.currentDirectory,
      outputDir: fileSystem.directory('out')..createSync(),
      defines: <String, String>{
        kBuildMode: 'debug',
      },
      inputs: <String, String>{
        kBundleSkSLPath: 'bundle.sksl',
      },
      processManager: processManager,
      artifacts: artifacts,
      fileSystem: fileSystem,
      logger: logger,
      engineVersion: '2',
    );
    environment.buildDir.createSync(recursive: true);
    fileSystem.file('bundle.sksl').writeAsStringSync(json.encode(
          <String, Object>{
            'engineRevision': '2',
            'platform': 'ohos',
            'data': <String, Object>{
              'A': 'B',
            },
          },
        ));

    // create pre-requisites.
    environment.buildDir.childFile('app.dill').writeAsStringSync('abcd');
    fileSystem
        .file(artifacts.getArtifactPath(Artifact.vmSnapshotData,
            mode: BuildMode.debug))
        .createSync(recursive: true);
    fileSystem
        .file(artifacts.getArtifactPath(Artifact.isolateSnapshotData,
            mode: BuildMode.debug))
        .createSync(recursive: true);
    await const DebugOhosApplication().build(environment);

    expect(
        fileSystem.file(fileSystem.path
            .join('out', 'flutter_assets', 'isolate_snapshot_data')),
        exists);
    expect(
        fileSystem.file(
            fileSystem.path.join('out', 'flutter_assets', 'vm_snapshot_data')),
        exists);
    expect(
        fileSystem.file(
            fileSystem.path.join('out', 'flutter_assets', 'kernel_blob.bin')),
        exists);
    expect(
        fileSystem.file(fileSystem.path
            .join('out', 'flutter_assets', 'io.flutter.shaders.json')),
        exists);
  });

  testUsingContext('OhosAot can build provided target platform', () async {
    processManager = FakeProcessManager.empty();
    final Environment environment = Environment.test(
      fileSystem.currentDirectory,
      outputDir: fileSystem.directory('out')..createSync(),
      defines: <String, String>{
        kBuildMode: 'release',
      },
      artifacts: artifacts,
      processManager: processManager,
      fileSystem: fileSystem,
      logger: logger,
    );
    processManager.addCommand(FakeCommand(
      command: <String>[
        artifacts.getArtifactPath(
          Artifact.genSnapshot,
          platform: TargetPlatform.ohos_arm64,
          mode: BuildMode.release,
        ),
        '--deterministic',
        '--snapshot_kind=app-aot-elf',
        '--elf=${environment.buildDir.childDirectory('arm64-v8a').childFile('app.so').path}',
        '--strip',
        environment.buildDir.childFile('app.dill').path,
      ],
    ));
    environment.buildDir.createSync(recursive: true);
    environment.buildDir.childFile('app.dill').createSync();
    environment.projectDir.childFile('.packages').writeAsStringSync('\n');
    const OhosAot ohosAot =
        OhosAot(TargetPlatform.ohos_arm64, BuildMode.release);

    await ohosAot.build(environment);

    expect(processManager, hasNoRemainingExpectations);
  });

  testUsingContext('OhosAot provide code size information.', () async {
    processManager = FakeProcessManager.empty();
    final Environment environment = Environment.test(
      fileSystem.currentDirectory,
      outputDir: fileSystem.directory('out')..createSync(),
      defines: <String, String>{
        kBuildMode: 'release',
        kCodeSizeDirectory: 'code_size_1',
      },
      artifacts: artifacts,
      processManager: processManager,
      fileSystem: fileSystem,
      logger: logger,
    );
    processManager.addCommand(FakeCommand(
      command: <String>[
        artifacts.getArtifactPath(
          Artifact.genSnapshot,
          platform: TargetPlatform.ohos_arm64,
          mode: BuildMode.release,
        ),
        '--deterministic',
        '--write-v8-snapshot-profile-to=code_size_1/snapshot.arm64-v8a.json',
        '--trace-precompiler-to=code_size_1/trace.arm64-v8a.json',
        '--snapshot_kind=app-aot-elf',
        '--elf=${environment.buildDir.childDirectory('arm64-v8a').childFile('app.so').path}',
        '--strip',
        environment.buildDir.childFile('app.dill').path,
      ],
    ));
    environment.buildDir.createSync(recursive: true);
    environment.buildDir.childFile('app.dill').createSync();
    environment.projectDir.childFile('.packages').writeAsStringSync('\n');
    const OhosAot ohosAot =
        OhosAot(TargetPlatform.ohos_arm64, BuildMode.release);

    await ohosAot.build(environment);

    expect(processManager, hasNoRemainingExpectations);
  });

  testUsingContext('kExtraGenSnapshotOptions passes values to gen_snapshot',
      () async {
    processManager = FakeProcessManager.empty();
    final Environment environment = Environment.test(
      fileSystem.currentDirectory,
      outputDir: fileSystem.directory('out')..createSync(),
      defines: <String, String>{
        kBuildMode: 'release',
        kExtraGenSnapshotOptions: 'foo,bar,baz=2',
        kTargetPlatform: 'ohos-arm',
      },
      processManager: processManager,
      artifacts: artifacts,
      fileSystem: fileSystem,
      logger: logger,
    );
    processManager.addCommand(FakeCommand(
      command: <String>[
        artifacts.getArtifactPath(
          Artifact.genSnapshot,
          platform: TargetPlatform.ohos_arm64,
          mode: BuildMode.release,
        ),
        '--deterministic',
        'foo',
        'bar',
        'baz=2',
        '--snapshot_kind=app-aot-elf',
        '--elf=${environment.buildDir.childDirectory('arm64-v8a').childFile('app.so').path}',
        '--strip',
        environment.buildDir.childFile('app.dill').path,
      ],
    ));
    environment.buildDir.createSync(recursive: true);
    environment.buildDir.childFile('app.dill').createSync();
    environment.projectDir.childFile('.packages').writeAsStringSync('\n');

    await const OhosAot(TargetPlatform.ohos_arm64, BuildMode.release)
        .build(environment);
  });

  testUsingContext(
      '--no-strip in kExtraGenSnapshotOptions suppresses --strip gen_snapshot flag',
      () async {
    processManager = FakeProcessManager.empty();
    final Environment environment = Environment.test(
      fileSystem.currentDirectory,
      outputDir: fileSystem.directory('out')..createSync(),
      defines: <String, String>{
        kBuildMode: 'release',
        kExtraGenSnapshotOptions: 'foo,--no-strip,bar',
        kTargetPlatform: 'ohos-arm',
      },
      processManager: processManager,
      artifacts: artifacts,
      fileSystem: fileSystem,
      logger: logger,
    );
    processManager.addCommand(FakeCommand(
      command: <String>[
        artifacts.getArtifactPath(
          Artifact.genSnapshot,
          platform: TargetPlatform.ohos_arm64,
          mode: BuildMode.release,
        ),
        '--deterministic',
        'foo',
        'bar',
        '--snapshot_kind=app-aot-elf',
        '--elf=${environment.buildDir.childDirectory('arm64-v8a').childFile('app.so').path}',
        environment.buildDir.childFile('app.dill').path,
      ],
    ));
    environment.buildDir.createSync(recursive: true);
    environment.buildDir.childFile('app.dill').createSync();
    environment.projectDir.childFile('.packages').writeAsStringSync('\n');

    await const OhosAot(TargetPlatform.ohos_arm64, BuildMode.release)
        .build(environment);
  });

  testWithoutContext('ohos aot bundle copies so from abi directory', () async {
    final Environment environment = Environment.test(
      fileSystem.currentDirectory,
      outputDir: fileSystem.directory('out')..createSync(),
      defines: <String, String>{
        kBuildMode: 'release',
      },
      processManager: processManager,
      artifacts: artifacts,
      fileSystem: fileSystem,
      logger: logger,
    );
    environment.buildDir.createSync(recursive: true);
    const OhosAot ohosAot =
        OhosAot(TargetPlatform.ohos_arm64, BuildMode.release);
    const OhosAotBundle ohosAotBundle = OhosAotBundle(ohosAot);
    // Create required files.
    environment.buildDir
        .childDirectory('arm64-v8a')
        .childFile('app.so')
        .createSync(recursive: true);

    await ohosAotBundle.build(environment);

    expect(
        environment.outputDir
            .childDirectory('arm64-v8a')
            .childFile('app.so')
            .existsSync(),
        true);
  });
}
